package com.zbkj.common.utils;

import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 高效全局唯一ID生成器
 * 生成13位以内纯数字ID
 * +----------------------------------------------------------------------
 * | CRMEB [ CRMEB赋能开发者，助力企业发展 ]
 * +----------------------------------------------------------------------
 */
public class IdGenerator {
    
    // 开始时间截 (2025-01-01)，用于减小时间戳位数
    private static final long START_TIMESTAMP = 1735660800000L;
    
    // 机器ID所占位数
    private static final long MACHINE_ID_BITS = 3L;
    
    // 序列号所占位数
    private static final long SEQUENCE_BITS = 5L;
    
    // 机器ID最大值 (2^3 - 1 = 7)
    private static final long MAX_MACHINE_ID = -1L ^ (-1L << MACHINE_ID_BITS);
    
    // 序列号最大值 (2^5 - 1 = 31)
    private static final long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BITS);
    
    // 机器ID向左移位数
    private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;
    
    // 时间戳向左移位数
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;
    
    // 机器ID (0-7)
    private final long machineId;
    
    // 序列号 (0-31)，使用AtomicInteger保证线程安全
    private final AtomicInteger sequence = new AtomicInteger(0);
    
    // 上次生成ID的时间戳
    private long lastTimestamp = -1L;
    
    // 单例实例
    private static volatile IdGenerator instance;
    
    /**
     * 获取单例实例
     */
    public static IdGenerator getInstance() {
        if (instance == null) {
            synchronized (IdGenerator.class) {
                if (instance == null) {
                    instance = new IdGenerator();
                }
            }
        }
        return instance;
    }
    
    /**
     * 私有构造函数，初始化机器ID
     */
    private IdGenerator() {
        this.machineId = getMachineId();
    }
    
    /**
     * 获取机器ID，基于MAC地址生成
     */
    private long getMachineId() {
        try {
            // 获取本机MAC地址
            StringBuilder sb = new StringBuilder();
            Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
            
            while (e.hasMoreElements()) {
                NetworkInterface ni = e.nextElement();
                byte[] mac = ni.getHardwareAddress();
                if (mac != null && mac.length > 0) {
                    for (byte b : mac) {
                        sb.append(String.format("%02X", b));
                    }
                    break;
                }
            }
            
            // 取MAC地址的hashCode并取模，确保在machineId范围内
            if (sb.length() > 0) {
                return Math.abs(sb.toString().hashCode() % (MAX_MACHINE_ID + 1));
            }
        } catch (SocketException e) {
            // 出现异常时使用随机数
            return (int) (Math.random() * (MAX_MACHINE_ID + 1));
        }
        
        // 默认情况下使用随机数
        return (int) (Math.random() * (MAX_MACHINE_ID + 1));
    }
    
    /**
     * 获取下一个ID
     */
    public synchronized long nextId() {
        // 获取当前时间戳
        long timestamp = System.currentTimeMillis();
        
        // 如果当前时间小于上一次生成ID的时间戳，说明系统时钟回退，抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("系统时钟回退，拒绝生成ID，回退时间: %d 毫秒", lastTimestamp - timestamp));
        }
        
        // 如果是同一时间生成的，则进行序列号递增
        if (lastTimestamp == timestamp) {
            // 获取新的序列号，如果超过最大值则重置为0
            int nextSequence = (sequence.incrementAndGet() & (int) MAX_SEQUENCE);
            if (nextSequence == 0) {
                // 序列号用完，等待下一毫秒
                timestamp = waitNextMillis(lastTimestamp);
            }
        } else {
            // 不同毫秒内，序列号重置为0
            sequence.set(0);
        }
        
        // 更新上次生成ID的时间戳
        lastTimestamp = timestamp;
        
        // 生成ID (时间戳 + 机器ID + 序列号)
        // 减去开始时间，减小时间戳位数
        long id = ((timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT) |
                  (machineId << MACHINE_ID_SHIFT) |
                  sequence.get();
        
        return id;
    }
    
    /**
     * 简化版接口，直接返回下一个ID
     */
    public static long next() {
        return getInstance().nextId();
    }
    
    /**
     * 等待下一毫秒
     */
    private long waitNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}
